package dsl.module.responder;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;

import dsl.common.domain.Action;
import dsl.common.domain.ActionType;
import dsl.common.domain.EventList;
import dsl.common.domain.Variable;
import dsl.common.domain.VariableList;
import dsl.common.exception.SemanticIsWrongException;
import dsl.utils.GUI;
import dsl.utils.MyLogger;

/**
 * 应答器，根据语法分析结果，与客户端进行对话
 * 实现Runnable接口，线程都应该有独立的变量表、事件表与函数指针，以实现高并发自动应答用户
 * 若有语义错误，通过{@link dsl.utils.GUI}以对话框方式告知用户
 * 
 * 使用说明：实现了Runnable接口，为这个应答器创建线程，即可高并发自动应答
 */
public class Responder implements Runnable {
    /**
     * 指向下一步要执行的操作，由事件指针和动作指针构成
     */
    private class PC {
        public int eventIndex;
        public int actionIndex;

        public PC(int eventIndex, int actionIndex) {
            this.eventIndex = eventIndex;
            this.actionIndex = actionIndex;
        }
    }

    /**
     * 告知客户端结束的信号量
     */
    private static final Integer END = 0;
    /**
     * 告知客户端不需要输入的信号量
     */
    private static final Integer NO_INPUT = 1;
    /**
     * 告知客户端需要输入的信号量
     */
    private static final Integer NEED_INPUT = 2;

    private Socket client;
    private EventList events;
    private VariableList variables;
    private ArrayList<PC> pcStack;

    /**
     * test用以模拟数据库操作
     */
    private HashMap<String, String> test;

    public Responder(Socket client, EventList events, VariableList variables) {
        this.client = client;
        this.events = events;
        this.variables = variables;
        pcStack = new ArrayList<>();
        pcStack.add(new PC(0, 0));
        test = new HashMap<>();
        test.put("张三", "15");
        test.put("李四", "16");
        test.put("王五", "17");
    }

    @Override
    public void run() {
        int operation = -1;
        String message = null;
        BufferedReader input;
        DataOutputStream output;
        try {
            input = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8"));
            output = new DataOutputStream(client.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        try {
            // 先取得下一步的操作类型，通知客户端是否需要输入，并抉择是否向用户输出信息
            while (true) {
                operation = getNextOperation();
                if (operation == -1)
                    break;
                else if (operation == ActionType.INPUT.ordinal()) {
                    sendMessage(output, NEED_INPUT.toString());
                    executeProgram(receiveMessage(input));
                } else {
                    message = executeProgram();
                    sendMessage(output, NO_INPUT + message);
                }
            }

            // 对话完成，结束线程，并且告知客户端进程结束
            sendMessage(output, END.toString());
            input.close();
            output.close();
            client.close();
            MyLogger.logger.info("finished one task");
        } catch (SemanticIsWrongException e) {
            // 发现语义错误，终止并告知客户端
            sendMessage(output, END + "program is wrong");
            GUI.showInfo("program is wrong");
        } catch (Exception e) {
            MyLogger.logger.warning("ERROR");
            e.printStackTrace();
        }
    }

    /**
     * 接受用户端发送的信息
     * 
     * @param input 输入流
     * @return String 接收到的信息
     */
    private String receiveMessage(BufferedReader input) {
        String str = null;
        try {
            while ((str = input.readLine()) == null)
                ;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return str;
    }

    /**
     * 向客户端发送信息
     * 
     * @param output  输出流
     * @param message 要输出的信息
     */
    private void sendMessage(DataOutputStream output, String message) {
        try {
            output.flush();
            output.write((message + "\n").getBytes("UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获得下一步要执行的操作类型
     * 
     * @return int 操作类型
     */
    private int getNextOperation() {
        PC pc = getPC();
        if (pc == null)
            return -1;
        int eventIndex = pc.eventIndex, actionIndex = pc.actionIndex;
        return events.get(eventIndex).actions.get(actionIndex).Operation;
    }

    /**
     * 执行input操作
     * 
     * @param str Input输入的字符串
     */
    private void executeProgram(String str) {
        PC pc = getPC();
        if (pc == null)
            return;
        int eventIndex = pc.eventIndex, actionIndex = pc.actionIndex;
        Action current = events.get(eventIndex).actions.get(actionIndex);
        Variable v = variables.findByName(current.Operand);
        v.value = str;
        pc.actionIndex++;
    }

    /**
     * 执行除input外的操作
     * 
     * @return String 要输出的字符串
     * @throws SemanticIsWrongException
     */
    private String executeProgram() throws SemanticIsWrongException {
        String str = "";
        PC pc = getPC();
        if (pc == null)
            return str;
        int eventIndex = pc.eventIndex, actionIndex = pc.actionIndex;
        Action current = events.get(eventIndex).actions.get(actionIndex);
        int operation = current.Operation;
        switch (operation) {
            // 执行output
            case 4:
                str = variables.getValueByName(current.Operand);
                break;
            // 执行then，将要跳转到的指针入栈
            // 未找到要跳转的函数，抛出语义错误：未找到相关函数
            case 6:
                int i = events.getIndexByName(current.Operand);
                if (i == -1)
                    throw new SemanticIsWrongException(events.get(eventIndex).name, current.Operand + " not found");
                pcStack.add(new PC(i, 0));
                break;
            // case语句，直接跳过
            case 5:
                pc.actionIndex++;
                break;
            // find语句，未查询到相关信息返回"not find"
            case 7:
                String s = variables.getValueByName(current.Operand);
                if (!test.containsKey(s))
                    str = "not found";
                else
                    str = test.get(s);
                break;
            // switch语句，找到要执行的语句
            case 8:
                String key = variables.getValueByName(current.Operand);
                actionIndex++;
                while (true) {
                    if (actionIndex >= events.get(eventIndex).actions.size())
                        break;
                    current = events.get(eventIndex).actions.get(actionIndex);
                    operation = current.Operation;
                    if ((operation == ActionType.CASE.ordinal()
                            && variables.getValueByName(current.Operand).equals(key))
                            || operation == ActionType.DEFAULT.ordinal()) {
                        actionIndex++;
                        break;
                    } else if (operation != ActionType.CASE.ordinal() && operation != ActionType.DEFAULT.ordinal()) {
                        break;
                    } else
                        actionIndex = actionIndex + 2;
                }
                pc.actionIndex = actionIndex - 1;
                break;
            // default语句，直接跳过
            case 9:
                pc.actionIndex++;
                break;
        }
        pc.actionIndex++;
        return str;
    }

    /**
     * 获取下一步要执行的指令
     * 
     * @return PC 执行下一步指令的指针
     */
    private PC getPC() {
        if (pcStack.size() <= 0)
            return null;
        PC pc = pcStack.get(pcStack.size() - 1);
        int eventIndex = pc.eventIndex, actionIndex = pc.actionIndex;
        while (actionIndex >= events.get(eventIndex).actions.size()) {
            pcStack.remove(pcStack.size() - 1);
            if (pcStack.size() <= 0)
                return null;
            pc = pcStack.get(pcStack.size() - 1);
            eventIndex = pc.eventIndex;
            actionIndex = pc.actionIndex;
        }
        return pc;
    }
}